from collections import defaultdict
from mbkEGES.Tools import MyTools
import numpy as np

class Graph():
    def __init__(self, click_df, user_colname, item_colname, time_colname, item_detail, sidenames, percent = 50):
        """
        click_df: 用户行为数据 dataframe格式
        item_detial: 物品详情数据 dataframe格式
        sidenames:表示边缘信息的列明, 例如 :['item_id', 'category', 'word_counts']
        user_colname: 字符串, click_df中userid的列名
        item_colname: 字符串, click_df中itemid的列名
        time_colname: 字符串, click_df中点击时间的列名
        percent: 时间节点的相邻间隔： 0——100%
        """
        self.mytooler = MyTools(click_df, user_colname, item_colname, time_colname)
        self.i2i = self.mytooler.user_behavior_sequences(percent)
        self.G = defaultdict(dict)
        self.index_dict = self.item_index() #存储itemname——itemindex
        self.inverse_dict = {wm[1]:wm[0] for wm in self.index_dict.items()} #存储itemindex——itemname
        self.item_detail = item_detail
        self.sidenames = sidenames
        self.side_information_dict, self.item_side_dict = self.get_side_information()

    #单向图(不排除出现双向节点)
    def one_way_G(self):
        for item1, item2 in self.i2i:
            item1 = self.transform(item1)
            item2 = self.transform(item2)
            self.G[item1][item2] = self.G[item1].get(item2,0)+1
        for itemid in self.G:
            self.G[itemid] = {item:value/sum(self.G[itemid].values()) for item, value in self.G[itemid].items()}
        return self.G
    #双向图(均是双向节点)
    def two_way_G(self):
        for item1, item2 in self.i2i:
            item1 = self.transform(item1)
            item2 = self.transform(item2)
            self.G[item1][item2] = self.G[item1].get(item2,0)+1
            self.G[item2][item1] = self.G[item2].get(item1,0)+1
        for itemid in self.G:
            sum_value = sum(self.G[itemid].values())
            self.G[itemid] = {item:value/sum_value for item, value in self.G[itemid].items()}
        return self.G
    
    #产生item的数字标签
    def item_index(self):
        index_dict = {}
        for item1, item2 in self.i2i:
            if item1 not in index_dict:
                index_dict[item1]=len(index_dict)
            if item2 not in index_dict:
                index_dict[item2] = len(index_dict)
        return index_dict

    def transform(self, itemnames):
        if isinstance(itemnames, list):
            return [self.index_dict[itemname] for itemname in itemnames]
        else:
            return self.index_dict[itemnames]
    
    def inverse(self, itemindexes):
        if isinstance(itemindexes, list):
            return [self.inverse_dict[itemindex] for itemindex in itemindexes]
        else:
            return self.inverse_dict[itemindexes]

    #获取边缘信息字典dict of side information
    def get_side_information(self):

        side_information_dict  = {} #存放边缘信息的字典库，内部存储所有边缘信息列的特征值所对应的index，方便embedding调用
        item_side_dict = {} #存储对应item的边缘信息
        for sidename in self.sidenames[1:]:
            side_information_dict[sidename]={}
            characteristic_categories = set(self.item_detail[sidename])
            for characteristic_category in characteristic_categories:
                side_information_dict[sidename][characteristic_category] = len(side_information_dict[sidename])

        def make_item_side_pair(df):
            return list(np.array([df[colname] for colname in self.sidenames[1:]]).T)
    
        item_side_df = self.item_detail.groupby(self.sidenames[0])[self.sidenames[1:]]\
                            .apply(lambda x: make_item_side_pair(x)).reset_index().rename(columns={0: 'side_information'})    
        side_dict = dict(zip(item_side_df[self.sidenames[0]], item_side_df['side_information']))
        for itemname in self.index_dict:
            item_side_dict[self.index_dict[itemname]]=\
                {self.sidenames[1:][index]:side_information_dict[self.sidenames[1:]\
                    [index]][side] for index, side in enumerate(side_dict[itemname][0].tolist())}

        return side_information_dict, item_side_dict